5.6 STM32驱动开发-读写Flash和模拟EEPROM
前言¶
这里主要讲解 Breeze Mini 的FLASH相关操作,完整代码在工程目录下的 Drivers 子目录下的 stm32f10x_driver_flash.c、stm32f10x_driver_flash.h、stm32f10x_driver_eeprom.c 和 stm32f10x_driver_eeprom.h 中,该代码主要实现对STM32自身的FLASH进行读写来模拟实现EEPROM的功能,进而存储控制算法所需的相关常数。
相关知识¶
ICP、ISP和IAP简介¶
ICP、ISP和IAP是对基于FLASH的IC进行烧写的3种方式,这里简单介绍一下3者之间的区别。
ICP¶
ICP(In-Circuit Programming)即 "在电路编程",指的是通过外接的仿真/下载器(JLink),通过特定的通信协议标准(JTAG或者SWD),可对所有FLASH空间进行烧写的一种编程方式。使用ICP方式烧写只需满足IC上电这个条件,结合MDK等IDE的话还可以进行在硬件单步调试,这是ICP编程的一个优势。
ISP¶
ISP(In-System Programming)即 "在系统编程",指的是不通过外接的仿真/下载器,只通过板载的通信接口(USART)将代码烧写到部分FLASH空间的一种编程方式。使用ISP方式烧写需满足IC和晶振上电,且预先要有一段程序已被烧写进去。
IAP¶
IAP(In-Application Programming)即 "在应用编程",指的是在MCU运行的状态下,不通过外接的仿真/下载器,只通过板载的通信接口(USART)将代码烧写到部分FLASH空间的一种编程方式。实现IAP方式时,FLASH空间需要有两块存储区,一块被称为BOOT区,一个被称为主程序区,程序上电时选择从BOOT区启动,此时若有对主程序区的编程需求,则会对其进行编程。否则程序指针会跳转到主程序区去执行代码。
3种方式总结¶
这里用一个比较形象的比喻来描述3者之间的区别:
-
ICP是把房子和地基都拆了重建,人需要等两者造好之后才能入住(主程序不能运行)
-
ISP是把房子拆了重建,但是地基还保留着,人需要等房子造好后才能入住(主程序不能运行)
-
IAP是对造好的房子进行内饰装修,此时人是可以入住的(主程序能运行)
STM32 FLASH简介¶
STM32不同型号的控制器的FLASH的容量也有所不同,最小的只有16K字节,最大的则有1024K字节,Breez Mini 选择的 STM32F103TBU6 的FLASH容量为128K字节,属于中等容量产品。中等容量产品的闪存模块结构如下图所示:
由上图可见,STM32的闪存模块由 主存储器、信息块 和 闪存存储器接口寄存器 3部分组成。
主存储器¶
主存储器用来存放代码和常数数据(如const
类型的数据),对于STM32F103TBU6来说,其主存储器每页上有1K字节,共128页。从上图可以看出主存储器的起始地址为 0x08000000。
信息块¶
该部分分为2个部分,其中 System memory 中存储ST官方自带的启动程序,是为了能够进行串口下载用的。Option Bytes 一般用于配置写保护、读保护等功能。
闪存存储器接口寄存器¶
该寄存器用于控制闪存读写。对主存储器和信息块的写入由内部的闪存编程擦除控制器管理,编程与擦除的高电压由内部产生。在执行闪存写操作时,任何对闪存的读操作都会锁住总线,在写操作完成后读操作才能正确地执行,即在进行写或擦除操作时,不能读取代码或数据。
闪存的读取¶
内置的闪存模块可以在通用地址空间直接寻址,任何32位数据的读操作都能够访问闪存模块的内容并得到相应的数据。读接口在闪存端包含一个读控制器,还包含一个AHB接口与CPU的衔接。这个接口的主要工作是产生读闪存的控制信号并预取CPU要求的指令块,预取指令块仅用于在I-Code总线上的取指操作,数据常量是通过D-Code总线访问的。这两条总线的访问目标是相同的闪存模块,访问D-Code将比预取指令优先级更高。
这里要特别注意闪存等待时间,因为CPU的运行速度比FLASH快得多,STM32F103的FLASH最快访问速度是不超过24MHz的,所以若CPU的工作频率超过24MHz,则必须设置FLASH的等待时间,我们这里使CPU工作在72MHz,则FLASH的等待周期就 必须设置为2,该设置是通过 FLASH_ACR 寄存器进行的。
比如我们要从地址addr中读取一个半字(16位)数据,则可以通过下行所示代码读取:
1 | data = *(vu16*) addr; |
该行代码将addr强制转换成vu16类型的指针,然后取该地址所指向的地址的值,即得到了addr地址上所存的值。类似地将 vu16 改成 vu8,即可读取指定地址的一个字节。
闪存的写入与擦除¶
闪存的写入和擦除操作相对读取操作要复杂一些。
编程¶
STM32的闪存编程是由FPEC(闪存编程和擦除控制器)模块处理的,这个模块包含7个32位寄存器,他们分别是:
-
FPEC键寄存器(FLASH_KEYR)
-
选择字节键寄存器(FLASH_OPTKEYR)
-
闪存控制寄存器(FLASH_CR)
-
闪存状态寄存器(FLASH_SR)
-
闪存地址寄存器(FLASH_AR)
-
选择字节寄存器(FLASH_OBR)
-
写保护寄存器(FLASH_WRPR)
其中FPEC键寄存器总共有3个键值:
-
RDPRT键 = 0X000000A5
-
KEY1 = 0X45670123
-
KEY2 = 0XCDEF89AB
STM32复位后FPEC模块是被保护的,不能写入FLASH_CR寄存器,通过写入特定的序列到FLASH_KEYR寄存器可以打开FPEC模块(KEY1和KEY2),STM32闪存编程每次必须写入16位数据,当FLASH_CR寄存器的PG位为1时,在一个闪存地址写入一个半字将启动一次编程,写入任何非法数据都会使得FPEC产生总线错误。在编程过程中(BSY位为1),任何读写闪存的操作都会使CPU暂停,直到此次闪存编程结束。在对STM32的FLASH进行编程时,必须要求其写入地址的FLASH是已经被擦除过的(值为0XFFFF),否则无法写入。STM32的FLASH编程流程为:
-
检查FLASH_CR的LOCK是否解锁,如果没有就先解锁
-
检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作
-
设置FLASH_CR的PG位为1
-
在指定地址写入指定数据
-
等待FLASH_SR的BSY位变为0
-
读出写入的地址并验证数据
擦除¶
由于STM32的FLASH写入操作需要保证写入的地址是已经被擦除过的,所以需要再介绍一下FLASH的擦除操作,STM32的FLASH擦除分为:页擦除和整片擦除,页擦除的工作流程为:
-
检查FLASH_CR的LOCK是否解锁,如果没有就先解锁
-
检查FLASH_SR的BSY位,以确认没有其他正在进行的编程操作
-
设置FLASH_CR的PER位为1
-
用FLASH_AR选择要擦除的页
-
设置FLASH_CR的STRT位为1
-
等待FLASH_SR的BSY位变为0
-
读出被擦除页并验证
相关寄存器¶
FLASH_KEYR寄存器¶
该寄存器各位描述如下所示:
该寄存器主要用于解锁FPEC,必须向该寄存器写入特定的序列(KEY1和KEY2)解锁后,才能够对FLASH_CR寄存器进行写操作。
FLASH_CR寄存器¶
该寄存器各位描述如下所示:
这里我们只讲解该寄存器的LOCK、STRT、PER和PG共4位。
-
LOCK位:该位用于指示FLASH_CR寄存器是否被锁住,该位在检测到正确的解锁序列后,会被硬件自动清零,在一次不成功的解锁操作后,在下次系统复位前,该位都不会再改变。
-
STRT位:该位用于开始一次擦除操作,向该位写1将执行一次擦除操作。
-
PER位:该位用于开始页擦除操作,向该位写1将设置页擦除模式。
-
PG位:该位用于开始编程操作,向该位写1将设置写FLASH模式。
FLASH_SR寄存器¶
该寄存器的各位描述如下图所示:
该寄存器主要用于指示当前FPEC的操作编程状态。
FLASH_AR寄存器¶
该寄存器的各位描述如下图所示:
该寄存器主要用于设置要擦除的页的地址。
固件库操作FLASH¶
操作FLASH相关的库函数在 FWLib 目录下的 stm32f10x_flash.c 和 stm32f10x_flash.h 中,但是 Breeze Mini 的飞控代码自己封装了一套操作FLASH的函数,在 Drivers 目录下的 stm32f10x_driver_flash.c 和 stm32f10x_driver_flash.h。
硬件连接¶
操作FLASH属于 Breeze Mini 主微控制器STM32F103TBU6的内部功能,是没有外部电路连接的。
软件设计¶
参考¶
- STM32开发指南-库函数版本_V3.1.pdf, 正点原子, ALLENTEK.